feat(typescript-angular): update enum definitions to use 'as const' for improved type safety#20958
Conversation
…or improved type safety fixes OpenAPITools#20809
|
@TiFu @taxpon @sebastianhaas @kenisteward @Vrolijkx @macjohnny @topce @akehir @petejohansonxo @amakhrov @davidgamero @mkusaka @joscha could you please review this? |
|
thanks, this should be backwards compatible, right? |
|
and whats the advantage of this change? the type union of the string values should already allow the compiler to check for valid values, no? |
|
@macjohnny yes, this is backwards compatible as the enum values have narrower type. The change of the enum -export type MyEnum = 'valueA' | 'valueB';
+export type MyEnum = typeof MyEnum [keyof typeof MyEnum];is not really relevant as both are equivalent - I just though it would be good to have a similar approach as typescript-fetch and typescript-axios. The main improvement is, that for the following generated enum: export const MyEnum = {
ValueA: 'valueA',
ValueB: 'valueB',
} as const;the compiler can infer that Subsequently this for example allows for correct "exhaustive check" in switch statements etc. This is as well the official typescript recommendation: https://www.typescriptlang.org/docs/handbook/enums.html#objects-vs-enums |
|
Just wanted to note that this is not fully backwards compatible due to different behaviors with type inference. // Old behavior
export type MyEnum1 = "valueA" | "valueB" | "valueC";
export const MyEnum1 = {
ValueA: "valueA" as MyEnum1,
ValueB: "valueB" as MyEnum1,
ValueC: "valueC" as MyEnum1,
};
function checkValue1(value: MyEnum1) {
const allowableValues = [MyEnum1.ValueA, MyEnum1.ValueB]; // inferred type: MyEnum1[]
return allowableValues.includes(value); // ok
}
// New behavior
export const MyEnum2 = {
ValueA: "valueA",
ValueB: "valueB",
ValueC: "valueC",
} as const;
export type MyEnum2 = typeof MyEnum2[keyof typeof MyEnum2];
function checkValue2(value: MyEnum2) {
const allowableValues = [MyEnum2.ValueA, MyEnum2.ValueB]; // inferred type: ("valueA" | "valueB")[]
return allowableValues.includes(value); // error: Argument of type 'MyEnum2' is not assignable to parameter of type '"valueA" | "valueB"'.
}
// Native TypeScript enums
export enum MyEnum3 {
ValueA = "valueA",
ValueB = "valueB",
ValueC = "valueC",
}
function checkValue3(value: MyEnum3) {
const allowableValues = [MyEnum3.ValueA, MyEnum3.ValueB]; // inferred type: MyEnum3[]
return allowableValues.includes(value); // ok
}The solution of course is to explicitly declare the type of |
fixes #20809
The description of #20809 details the change :)
PR checklist
Commit all changed files.
This is important, as CI jobs will verify all generator outputs of your HEAD commit as it would merge with master.
These must match the expectations made by your contribution.
You may regenerate an individual generator by passing the relevant config(s) as an argument to the script, for example
./bin/generate-samples.sh bin/configs/java*.IMPORTANT: Do NOT purge/delete any folders/files (e.g. tests) when regenerating the samples as manually written tests may be removed.
master(upcoming7.x.0minor release - breaking changes with fallbacks),8.0.x(breaking changes without fallbacks)